/*
  S.M.A.C.K - An operating system kernel
  Copyright (C) 2010,2011 Mattias Holm and Kristian Rietveld
  For licensing and a full list of authors of the kernel, see the files
  COPYING and AUTHORS.
*/

#include <idle.h>
#include <heap.h>
#include <hal.h>
#include <slaballoc.h>
#include <list.h>

typedef struct _idle_handler_t idle_handler_t;

struct _idle_handler_t {
  idle_handler_func_t handler;
  void *handler_data;

  LIST_ENTRY(_idle_handler_t) handlers;
};

typedef LIST_HEAD(_idle_handler_t) idle_handler_list_t;

slab_t idle_slab = SLAB_INITIALISER(idle_handler_t);
static idle_handler_list_t idle_handlers = { NULL, NULL };


static void
idle_run_idle_handlers(void)
{
  idle_handler_t *entry;

  if (LIST_EMPTY(idle_handlers))
    return;

  LIST_FOREACH(entry, idle_handlers, handlers)
    {
      (* entry->handler) (entry->handler_data);

      /* We can safely remove the previous entry here. */
      if (LIST_PREV(entry, handlers))
        {
          idle_handler_t *prev = LIST_PREV(entry, handlers);
          LIST_REMOVE(idle_handlers, prev, handlers);
          slab_free(prev);
        }
    }

  /* Now remove the last entry */
  if (!LIST_EMPTY(idle_handlers))
    {
      entry = LIST_FIRST(idle_handlers);
      LIST_REMOVE(idle_handlers, entry, handlers);
      slab_free(entry);
    }
}

void
idle_register_idle_handler(idle_handler_func_t  handler,
                           void                *handler_data)
{
  idle_handler_t *entry = slab_alloc(&idle_slab);

  entry->handler = handler;
  entry->handler_data = handler_data;

  LIST_APPEND(idle_handlers, entry, handlers);
}

void
idle(void)
{
  while (1)
    {
      idle_run_idle_handlers();
      hw_idle();
    }
}
